ActivityManagerService
ActivityManagerService(简称AMS)是Android系统最核心的Binder服务之一,从名称上看好像它只是Activity的管理者,但实际上AMS管理和维护着系统四大组件(Activity,Service,Broadcast,ContentProvider)的相关工作,这个任务非常重要而且异常复杂,在KK中单是AMS一个类的源码行数就高达一万六千多行,可见其内部的任务有多庞大。在这之中,AMS对于Activity的管理任务最为复杂,这可能也是它为什么叫ActivityManagerService的原因吧。AMS对于activity的管理实际上通过三个类来体现,它们分别是ActivityRecord,TaskRecord以及ActivityStack,把握和理解它们之间的关系对于我们理解AMS的工作将会有很大的帮助。因此本篇将会介绍这三者在AMS中的联系和作用。
ActivityRecord
ActivityRecord是应用层Activity组件在AMS中的代表,每一个在应用中启动的Activity,在AMS中都有一个ActivityRecord实例来与之对应,这个ActivityRecord伴随着Activity的启动而创建,也伴随着Activity的终止而销毁。
类结构说明
关于ActivityRecord类,它只是一个简单的类,并没有继承任何其他父类,在它内部存储的都是一些关于Activity组件的相关信息,所以类结构比较简单,下面我将列出它的一些成员进行介绍:
1 | /** |
可以看出ActivityRecord包含了很多信息,我们简单描述下:
- appToken :这是一个Binder,而且是Server端的实体,Token即令牌,它一般作为一个标识使用,这个appToken用来在WindowManagerService端标识这个AcitvityRecord,token的实现如下
1 | static class Token extends IApplicationToken.Stub { |
可以看到它实际上是一个Token,实现了自IApplicationToken.Stub的方法,内部有个弱引用指向包含它的ActivityRecord实例,从实现来看,appkToken实际上有两个作用,在WMS端标记一个ActivityRecord,同时可以通过该token和AMS进行通信。
2. launchedFromUid和launchedFromPackage 启动该activity的应用uid和包名,比如我们从launcher启动的应用activity,这个对应的就是launcher的uid和包名
3. intent 和 ComponentName intent是启动时候传递的意图对象,ComponentName标记了启动的acitivity组件。
4. packageName和processName 分别代表了activity所属应用的包名和进程名
5. taskAffinity 标记了activity的亲属性,默认情况下为包名,这个成员和该activity所属TaskRecord有关,后面我们具体介绍。
6. logo 和 theme 这些是activity所使用的logo和theme资源标记。
7. task 应用所属TaskRecord,后面介绍。
8. resultTo和requestCode,resultTo也是一个ActivityRecord,它是当前ActivityRecord对应的activity要返回和回复的activity,我们通过startActivityForResult启动一个activity时,往往需要接受来自于启动的activity的返回值,这个resultTo就是负责处理返回值的activity对应的ActivityRecord
9. app 它是一个ProcessRecord,用于描述当前activity所在应用的进程
10. state 它是一个AcitivityState枚举,用于描述当前activity的状态,这些状态被AMS内部使用,ActivityState的定义如下:
1 | enum ActivityState { |
- launchMode activity的启动模式
ActivityRecord的创建
1 | final int startActivityLocked(IApplicationThread caller, |
AcitivtyRecord是在启动activity时候创建的,这里具体是在ActivityStackSupervisor中,关于ActivityStackSupervisor后面再介绍。我们看看ActivityRecord的构造方法中
1 | ActivityRecord(ActivityManagerService _service, ProcessRecord _caller, |
在构造方法中创建了ActivityRecord的appToken,关于appToken我们前面已经做了介绍。初始的activity状态为ActivityState.INITIALIZING,这里的visible标记的是activity在WMS端对应的window是否需要显示,在构造ActivityRecord时表示我们要启动activity了,这时候也就需要WMS为我们显示窗口,置true。nowVisible表示的是activity当前的窗口显示状态,这时候activity还在创建过程中,还未显示出来,置false。
TaskRecord
TaskRecord即任务栈,或者叫返回栈(back Stack),Task实际上是指执行一个特定任务时和用户进行交互的一组activity,什么意思呢?比如我们为了完成用微信发送消息这个任务,而需要打开首页的联系人列表,然后选择一个联系人进入聊天界面发送消息,这两个activity就可以说是一个Task,但如果我们说要发送图片,那么还会去打开选择照片的activity,这时候Task就是三个activity,所以说Task是一个动态的概念,但不管怎么样,我们总成为它是一组activity的集合,这个集合在AMS中就是TaskRecord,它和数据结构中的Stack结构类似,也是先进后出,当我们打开App中的一个activity1时,它对应的ActivityRecord被添加到TaskRecord中,再在activit1中打开第二个页面activity2,在activity2中打开activity3,同样它们的ActivityRecord也会被添加到TaskRecord,当我们按下返回键,activity3先从TaskRecord中弹出来,依次类推,当task中所有的activity被弹出后,这个task也就销毁了。最终它的结构如下图所示:
Task切换
一般情况下,我们可以将打开的一个app看做是一个TaskRecord,当我们先后打开app A的两个页面A和B,这时候在其对应的TaskRecord中就有两个ActivityRecord的记录,当我们按下home键后,然后打开另一个app B的C和D页面,这时候app A的TaskRecord就到了后台中,它内部的两个activity此时都处于停止状态,而app B的TaskRecord此时处于前台和用户交互的状态,它的TaskRecord中也有两个ActivityRecord的记录,如果我们再按下home键,点击app A,这时候我们发现app A停留在B页面,也就是说app A的TaskRecord又作为一个整体从后台切换到了前台,它的状态和之前是一样的。系统可以同时管理多个后台的TaskRecord,但是如果用户打开的太多app在后台中,系统可能会回收一部分。
打开多个app时,AMS中就存在了多个TaskRecord,我们可以在这些TaskRecord之间进行切换,当我打开Email应用后可以在编辑Email的过程中打开相册应用来选择图片,选择完成后可以继续回到Email中编辑邮件。这个是Task切换带来的便利。
启动模式launchMode
Android管理activity的方式是在同一个Task中使用先进后出的栈管理方式,默认情况下,我们创建一个Activity就会在TaskRecord中有一个实例,但有些时候我们可能希望当启动一个activity的时候使用TaskRecord中已经存在的实例或者当我们启动一个activity时需要将它放置在单独的一个TaskRecord之中,这时候就需要我们为启动的activity设置启动模式(launchMode)了,所以说launchMode是和TaskRecord相关联的。
有四种不同的启动模式:
- standard,这个即是Android默认的启动模式,每打开一个Activity就创建一个实例到Task中,同一个Activity在Task中可以有多个实例。
- singleTop,如果一个activity的实例已经在Task栈的顶部,那么当再次打开这个activity时会复用这个实例而不再创建一个新的实例,同时通过onNewIntent传递给这个实例Intent。比如Task已经有四个activityA->B->C->D,D在Task顶部,那么再次打开D页面时,Task依然是A->B->C->D,而在Standard模式下将会是A->B->C-D->D
- singleTask,这个是说一个activity的实例在Task只能有一个实例,如果一个activity实例已经在Task中存在,Android系统会直接把Intent路由到Task中存在的Activity实例对象上,会通过调用onNewIntent()方法处理,而非创建一个新的Activity实例对象。并且会把在它之上的其它Activity清理掉。但是如果实例不存在Task中,此时系统将会为比较调用方activity和被调用方activity的taskAffnity,如果一致就将该activity创建一个实例,并加入到已存在的Task中,如果不一致,就为该activity创建一个新的Task,此时activity是作为Task的root activity的。事实上,Task的taskAffinity正是由Task的root activity决定的。
- singleInstance,如果设置了该模式,那么在Task中只能有一个该activity的实例。
启动模式不仅可以在AndroidManifest中进行设定,同样的我们可以使用Intent Flags进行设定,一些常见的Flags可以更方便和灵活的处理activity和Task的关系:
- FLAG_ACTIVITY_NEW_TASK ,这个标记同singleTask的处理方式
- FLAG_ACTIVITY_SINGLE_TOP,这个标记同singleTop的处理方式
- FLAG_ACTIVITY_CLEAR_TOP,如果activity的实例已经在当前Task中,那么再启动时,将会将其之上的activity实例都销毁掉。比如Task A->B->C->D,如果在D中启动B添加了该标记,那么会将C和C都从Task销毁掉,如果此时B是默认的启动模式,那么会将B的实例也销毁,并重新创建一个实例添加到Task顶部,如果我们不想它重新创建,可以配合FLAG_ACTIVITY_SINGLE_TOP使用,这样复用顶部的B实例并触发onNewIntent。
- FLAG_ACTIVITY_REORDER_TO_FRONT,新启动的Activity将会被放到它所属task的顶部,例如,当Task A->B->C->D,如果D启动B使用了这个标记,B将会排在这个task的最上面,也即现在Task的顺序变成了A,C,D,B。
- FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET 这个标记将会在TASK重置时清理对应的Activity,比如Task当前为A->B,A启动B添加了该标记,那么当切换到Home再打开该应用后Task变为A,B被清理。这个其实是在Task中设置了一个还原点,当Task重置时将Task恢复到指定的还原点。
另外在 AndroidManifest的
-
android:allowTaskReparenting
这个属性定义的是activity具有reparent Task的能力,即标记一个Activity实例在当前应用退居后台后,是否能从启动它的那个Task移动到有共同TaskAffinity的Task,“true”表示可以移动,“false”表示它必须呆在当前应用的task中,默认值为false。下面通过一个例子说明这种情况:
比如App A 有一个Activity:MainActivity,App B有两个Activity:BMainActivity和SecondActivity,其中SecondActivity定义了该属性为true,当我们在App A中的MainActivity中启动App B的SecondActivity后,此时A的Task为MainActivity->SecondActivity,注意此时MainActivity和SecondActivity的TaskAffinity并不是相同的,他们为各自的包名。如果此时按Home键回到Launcher,再启动App B后,会发现此时的页面是SecondActivity,也就是说SecondActivity从原来App A的Task移动到了和他TaskAffinity相同的Task中即App B的Task。 -
android:clearTaskOnLaunch
如果设置了这个属性为true,每次用户重新启动这个应用时,都只会看到Root Activity,task中的其他Activity都会被清除出栈。这个属性也只对Root Activity有效。 -
android:alwaysRetainTaskState
这个属性是让Task保持原来的状态,true表示保持,false不能够保持,此属性也只对Root Activity有效,一般情况下,如果Task切换到后台后太久系统会对Task进行清理,除了Root Activity其他Activity都会被清理。但如果设置了该属性为true,则可以保持上次操作的界面。 -
android:noHistory
这个属性设置为true后,则Activity将不会被记录到Task的history列表中,那么就是说,这个Activity不可见,那么它实际上就和销毁了一样,因为AMS没有它的相关信息。
TaskRecord的实现
了解了Task的相关知识,接下来我们看看它是如何在AMS中实现的。
1 | final class TaskRecord extends ThumbnailHolder { |
在AMS中,Task是通过TaskRecord类来描述的,它的成员相对ActivityRecord要少很多,这里我们介绍下:
- taskId,这个是TaskRecord的一个唯一标记
- affnity,亲属性的名称,同一个Task的ativity有相同的taskAffinity,它是由Task的Root Activity的taskAffinity决定的。
- Intent,启动这个Task的源activity对应的Intent,也即是启动root activity的Intent。
- numActivities,这个是当前Task中的activity数目。
- numFullscreen 全屏activity的个数
- mActivities 维护的ActivityRecord实例,这个代表的就是Activity,可见TaskRecord栈结构是通过ArrayList来体现的。
- stack ,所属的ActivityStack,关于ActivityStack我们后面介绍
TaskRecord的创建
一般情况下,在启动App的第一个activity时,AMS为其创建一个TaskRecord任务栈,当然启动后也可能创建新的TaskRecord,比如我们启动singleTask的Activity,而且为该Activity指定了和包名不同的taskAffinity,这时候也会为该activity创建一个新的TaskRecord,所有一个App是可能有多个TaskRecord存在的,这取决于应用的使用场景和需求。
我们按照分析的逻辑看看AMS中是如何实现为TaskRecord创建的。
1 | final int startActivityUncheckedLocked(ActivityRecord r, |
当启动模式设定为singleTask后我们通过findTaskLocked来为启动的Activity寻找TaskRecord,如果找到则返回它顶部的Activity对应的ActivityRecord。
1 | ActivityRecord findTaskLocked(ActivityRecord r) { |
在findTaskLocked中,我们会比较要启动的Activity和Task的affinity,如果匹配就返回TaskRecord顶部的ActivityRecord,如果最终没找到则返回null。如果返回值intentActivity为null,那么
addingToTask和reuseTask都不会设置,这时候会通过createTaskRecord创建TaskRecord并设置到ActivityRecord之中。
TaskRecord的复用
事实上大多数情况,Activity都会复用TaskRecord,也就是说Activity会添加到相同的TaskRecord之中,除了应用第一次启动或者taskAffinity不同之外。如果上一步中通过findTaskLocked返回的intentActivity不为null,这表示我们为启动模式为singleTask的activity找到了一个可复用的TaskRecord。
1 | final int startActivityUncheckedLocked(ActivityRecord r, |
ActivityStack
ActivityStack的说法听起来很容易和我们常说的任务栈混淆,很多人可能看到它的第一个反应是它就是我们的任务栈,然而并非如此,我们知道系统中可能同时有多个Task,一般前台就一个Task和用户进行交互,而后台中可能有多个Task存在,前后台的Task可以进行切换,AMS为了方便的管理这些Task而引入了ActivityStack,在ActivityStack内部通过ArrayList维护了一组TaskRecord。一般来说Launcher的Task属于单独的一个ActivityStack,称为Home Activity Stack,System UI如rencentActivity的Task属于一个单独ActivityStack,其他App的Task属于另一个ActivityStack。
结构
下面我们看看ActivityStack的结构
1 | final class ActivityStack { |
从其成员中我们可以看出ActivityStack维护了一组TaskRecord,以及一组最近使用的Activities,还有上次切换ActivityStack被暂停的Activity,和TaskRecord类似,多个ActivityStack同样是可以进行切换的,比如我们从桌面Launcher点击app后,会从Launcher所在的ActivityStack切换到app所在的ActivityStack,mResumedActivity表示正在运行的activity,它也可以是null,当为null时表示当前运行的Activity不在该ActivityStack。同样的,在ActivityStack中通过mStackId来唯一的标识一个ActivityStack。
明白了ActivityStack的概念和用途,我们看看ASM是如何管理它的。在AMS中通过ActivityStackSupervisor来管理这些ActivityStack,从而间接的管理着TaskRecord,在ActivityStackSupervisor内部通过mHomeStack和mStacks管理着所有ActivityStack,其中mHomeStack是Launcher的TaskRecord所在的ActivityStack,而mStacks是其他的ActivityStack,它是一个ArrayList
1 | public final class ActivityManagerService extends ActivityManagerNative |
1 | public final class ActivityStackSupervisor { |
- mLastStackId是所有ActivityStack的计数,这个在创建ActivityStack使用来计算stackId
- mCurTaskId 指当前前台的ActivityStatck对应的stackId
- mHomeStack 这个是launcher的TaskRecord所在的ActivityStack。
- mStacks 所有非Launcher的ActivityStack。这是一个ArrayList,因为非Launcher的ActivityStack可能有多个。
- 除了以上成员,ActivityStackSupervisor还维护着多组ActivityRecord的集合,这些分别用来记录那些等待显示,或者正在停止或销毁的Activity。
创建过程
同样的ActivityStack也是按需创建的
1 | final int startActivityUncheckedLocked(ActivityRecord r, |
实际上ActivityStack的创建时在Launcher启动时候进行的,而且是在系统启动后启动的第一个app,这个app在启动第一个Activity的时候在AMS还未有非Launcher的ActivityStack,所以需要创建一个。后面的App启动时就会找到这个ActivityStack。创建时通过ActivityStackSupervisor的adjustStackFocus方法,这个方法为要启动的Acitivity寻找前台的ActivityStack,即mFocusedStack。
1 | //frameworks/base/services/java/com/android/server/am/ActivityStackSupervisor.java |
在adjustStackFocus方法中,对于应用Task,如果ActivityRecord对应的TaskRecord所属的ActivityStack不是当前的mFocusedStack,则将其所属的ActivityStack作为mFoucnedStack返回则直接返回,否则如果此时TaskRecord还未创建,如果mFocusedStack已经不为null,则直接返回,否则会从当前的mStacks找到一个非Home Stack作为mFocusedStack返回,如果以上都不满足,则为当前系统创建一个ActivityStack作为mFocusedStack返回。这是通过调用AMS的createStack方法来完成的结果返回stackId。
1 |
|
createStack是作为AMS的Binder接口提供给使用者的,这里会进一步通过ActivityStackSupervisor的createStack方法创建。
1 | int createStack() { |
通过mLaskStackId来计算要创建的ActivityStack对应的id,然后将新创建的实例添加到mStacks中。
Task切换
1 | final int startActivityUncheckedLocked(ActivityRecord r, |
Task的切换同样是在startActivityUncheckedLocked中进行的,首先我们通过findTaskLocked为要启动的activity寻找一个合适的TaskRecord,通过initActivity标识,找到后这个TaskRecord就是要切换到前台的Task,通过getLastStack取到上次切换的ActivityStack,然后取到它顶部的ActivityRecord,对于Launcher启动来说,这个就是非Launcher Stack,它顶部的Task可能不是initAcitivty标识的那个,因为可能之前我们启动的app的TaskRecord和它是不同的,即curTop.task != intentActivity.task,那么这时候我们需要为r设置Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT,同时通过moveTaskToFrontLocked将Task移动到ActivityStack的顶部。
1 | final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord reason, Bundle options) { |
moveTaskToFrontLocked通过insertTaskAtTop将TaskRecord移动到顶部
1 | private void insertTaskAtTop(TaskRecord task) { |
insertTaskAtTop先从mTaskHistory中删除该TaskRecord,然后计算添加的位置stackNdx,最后添加到mTaskHistory中,这里最顶部的Task一般存放在最末尾。
总结
从以上的分析中,我们知道了ActivityRecord、TaskRecord以及ActivityStack之间的关系及作用,它们之间的组织关系实际上是包含的关系,即AcitivtyStack包含了TaskRecord的集合,TaskRecord包含着ActivityRecord的集合,同时反向的,ActivityRecord中记录了它所属的TaskRecord,TaskRecord记录着它所属的ActivityStack。他们三者贯穿AMS管理Activity的整个逻辑处理,其中ActivityRecord是Activity组件在AMS中的存在的形式,它和Activity实例是一一对应的关系(注意这里描述的是实例),TaskRecord用于描述Task,即一组Activity,这组Activity以栈的形式组织起来,每个启动的Activity即ActivityRecord都应该要有所属的TaskRecord。ActivityStack是系统中用于管理TaskRecord的,系统中可能有多组ActivityStack,大体可以分为Launcher ActivityStack和非Launcher ActivityStack,我们启动的App对应的TaskRecord由非Launcher ActivityStack管理,它是在系统启动第一个app时创建的。
以上都是个人的理解,有描述不当的地方,烦请不吝赐教~
参考
Understand Tasks and Back Stack
https://developer.android.com/guide/components/activities/tasks-and-back-stack
AOSP